﻿//  -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	Smart.Common
//  File name:	WebSecurableType.cs
//  Developer:	Solomatov Igor
//  Created:	15.04.2012
// 
//  -------------------- iSharePoint -------------------- //

using System;
using System.Collections.Generic;
using System.ComponentModel;
using Microsoft.SharePoint;
using Smart.Common.Entities;

namespace Smart.Common.Security
{
	/// <summary>
	/// SharePoint securable
	/// </summary>
	[Localizable(false)]
	public class WebSecurableType : SecurableType
	{
		/// <summary>
		/// SharePoint securable instance
		/// </summary>
		public static WebSecurableType Instance = new WebSecurableType();

		/// <summary>
		/// Enter security context
		/// </summary>
		public override void EnterSecurityContext()
		{
			SPH.EnterElevatedMode();
		}

		/// <summary>
		/// Leave security context
		/// </summary>
		public override void LeaveSecurityContext()
		{
			SPH.LeaveElevatedMode();
		}

		/// <summary>
		/// Checks whether security assignments inherited from parent securable object
		/// </summary>
		public override bool IsSecurityInherited(ISecurable securable)
		{
			var entity = (WebEntity)securable;
			var item = entity.GetItem();

			return !item.SafeHasUniqueRoleAssignments();
		}

		/// <summary>
		/// Make security inherit from parent securable object
		/// </summary>
		public override void InheritSecurity(ISecurable securable)
		{
			var entity = (WebEntity)securable;
			var item = entity.GetItem();

			using (new UnsafeUpdate(item.Web))
				item.ResetRoleInheritance();
		}

		/// <summary>
		/// Break security inheritance from parent securable object
		/// </summary>
		public override void BreakSecurityInheritance(ISecurable securable, bool copyAssignments)
		{
			var entity = (WebEntity)securable;
			var item = entity.GetItem();

			using (new UnsafeUpdate(item.Web))
				item.BreakRoleInheritance(copyAssignments);
		}

		/// <summary>
		/// Ensure securable security assignments not more and not less then specified, except system security settings
		/// </summary>
		/// <returns>true, if security changed</returns>
		public override bool EnsureSecurityExact(ISecurable securable, ICollection<SecurityAssignment> assignments, bool currentScopeOnly)
		{
			var entity = (WebEntity)securable;
			var item = entity.GetItem();
			var web = item.Web;

			using (new UnsafeUpdate(web))
			{
				List<Cortege<SPPrincipal, string>> securityAssignments;

				ConvertAssignments(web, assignments, out securityAssignments);

				return item.EnsureRoleAssignments(web, securityAssignments, currentScopeOnly, true);
			}
		}

		/// <summary>
		/// Ensure securable security assignments not less then specified, except system security settings
		/// </summary>
		/// <returns>true, if security changed</returns>
		public override bool EnsureSecurityInherit(ISecurable securable, ICollection<SecurityAssignment> assignments, bool currentScopeOnly)
		{
			var entity = (WebEntity)securable;
			var item = entity.GetItem();
			var web = item.Web;

			using (new UnsafeUpdate(web))
			{
				List<Cortege<SPPrincipal, string>> securityAssignments;

				ConvertAssignments(web, assignments, out securityAssignments);

				return item.EnsureRoleAssignments(web, securityAssignments, currentScopeOnly, false);
			}
		}

		/// <summary>
		/// Converts SecurityAssignment collection to SharePoint security assignments
		/// </summary>
		/// <exception cref="ArgumentException">Can't find principal specified in <paramref name="assignments"/></exception>
		protected virtual void ConvertAssignments(SPWeb web, IEnumerable<SecurityAssignment> assignments, out List<Cortege<SPPrincipal, string>> securityAssignments)
		{
			securityAssignments = new List<Cortege<SPPrincipal, string>>();

			var principalCache = new Dictionary<string, SPPrincipal>(StringComparer.OrdinalIgnoreCase);

			foreach (var assignment in assignments)
			{
				var principal = principalCache.GetPrincipalAndEnsureCached(web, assignment.Principal);

				if (principal == null)
					throw new ArgumentException(String.Concat("Can't find principal ", assignment.Principal, "!"), "assignments");

				securityAssignments.Add(new Cortege<SPPrincipal, string>(principal, assignment.AccessLevel));
			}
		}
	}
}